#include "Controller.h"
#include "CParams.h"
#include <QGraphicsScene>
#include <QPainter>
#include <QStyleOption>
#include <QKeyEvent>

vector<SVector2D> Controller::v_mines;

Controller::Controller(QGraphicsScene *sc) : ticks(0), generation(0)
{
    scene = sc;
    gen = "Generation: ";
    genNum.setNum(generation);
    genText = new QGraphicsTextItem(gen + genNum);
    genText->setPos(-20,-35);
    scene->addItem(genText);

    sTicks = "Ticks: ";
    sTicksNum = QString(ticks + 48);
    ticksText = new QGraphicsTextItem(sTicks + sTicksNum);
    ticksText->setPos(CParams::iWindowWidth - 45,-35);
    scene->addItem(ticksText);

    sFitness = "Greatest Fitness: ";
    fitnessNum = QString(48);
    fitnessText = new QGraphicsTextItem(sFitness + fitnessNum);
    fitnessText->setPos(CParams::iWindowWidth/4, -35);
    scene->addItem(fitnessText);

    sAvgFitness = "Average Fitness: ";
    avgFitnessNum = QString(48);
    avgFitnessText = new QGraphicsTextItem(sAvgFitness + avgFitnessNum);
    avgFitnessText->setPos(CParams::iWindowWidth/1.6, - 35);
    scene->addItem(avgFitnessText);

    a_mines = new mines *[CParams::iNumMines];
    mineSweepers = new Minesweeper *[CParams::iNumSweepers];
    startTimer(1000 / 33);
}

Controller::~Controller()
{
    for(int i = 0; i < CParams::iNumMines; i++)
        if(a_mines[i] != NULL)
            delete a_mines[i];
    delete[] a_mines;
}

void Controller::timerEvent(QTimerEvent *)
{
    if(ticks <= CParams::iNumTicks)
    {
        for(int i = 0; i < CParams::iNumSweepers; i++)
            mineSweepers[i]->update();

        checkCollision();
        ++ticks;
        sTicksNum.setNum(ticks);
        ticksText->setPlainText(sTicks + sTicksNum);

    }
    else if(generation < CParams::iMaxGen)
    {
        //m_vecAvFitness.push_back(m_pGA->AverageFitness());
        //m_vecBestFitness.push_back(m_pGA->BestFitness());

        //increment the generation counter
        ++generation;

        //reset cycles
        ticks = 0;

        vecThePopulation.clear();

        for(int i = 0; i < CParams::iNumSweepers; ++i)
            vecThePopulation.push_back(SGenome(mineSweepers[i]->getWeights(),mineSweepers[i]->getFitness()));


        //run the GA to create a new population
        vecThePopulation = gAlgo->Epoch(vecThePopulation);

        //insert the new (hopefully)improved brains back into the sweepers
        //and reset their positions etc
        for (int i=0; i<CParams::iNumSweepers; ++i)
        {
                mineSweepers[i]->putWeights(vecThePopulation[i].vecWeights);
                mineSweepers[i]->Reset();
        }
        output.open("statistics.txt", std::ios_base::app);
        output<<"Generation: "<<generation<<endl;

        genNum.setNum(generation);
        genText->setPlainText(gen + genNum);

        int ghf = (int)getHighestFitness();
        output<<"Highest Fitness: "<<ghf<<endl;

        fitnessNum.setNum(ghf);
        fitnessText->setPlainText(sFitness + fitnessNum);

        double gaf = getAvgFitness();
        output<<"Average Fitness: "<<gaf<<endl<<endl;

        avgFitnessNum.setNum(gaf);
        avgFitnessText->setPlainText(sAvgFitness + avgFitnessNum);

        output.close();
    }
    else
    {
        output.open("statistics.txt", std::ios_base::app);
        output<<"Best Minesweeper"<<endl;
        output<<"Fitness: "<<bestSweeper.dFitness<<endl;
        output<<"Weights: "<<endl;
        for(int i = 0; i < bestSweeper.vecWeights.size(); ++i)
            output<<bestSweeper.vecWeights[i]<<endl;
        this->destroy(true);
    }
}

void Controller::keyPressEvent(QKeyEvent *k)
{
    int i = tolower(k->key());
    switch(k->key())
    {
    case Qt::Key_R:
            generation = 0;
            ticks = 0;
            for(int i = 0; i < CParams::iNumSweepers; ++i)
                mineSweepers[i]->Reset();
            break;
    case Qt::Key_F:
            break;
    }
}

void Controller::mousePressEvent(QMouseEvent *event)
{
    printf("\nMinesweeper in board");
        setFocus();

}

void Controller::init()
{
    for(int i = 0; i < CParams::iNumMines; i++)
    {
        a_mines[i] = new mines();
        double x = (RandFloat() * CParams::iWindowWidth);
        double y = (RandFloat() * CParams::iWindowHeight);
        a_mines[i]->setPos(x,y);
        v_mines.push_back(SVector2D(x,y));
    }
    int a = CParams::iNumSweepers;
    for (int i = 0; i < CParams::iNumSweepers; ++i) {
        mineSweepers[i] = new Minesweeper();
        SVector2D pos(RandFloat() * CParams::iWindowWidth, RandFloat() * CParams::iWindowHeight);
        mineSweepers[i]->setPos(pos.x,pos.y);
        mineSweepers[i]->setPosition(pos);
        mineSweepers[i]->setMines(v_mines);
     }

    numWeightsInNN = mineSweepers[0]->GetNumOfWeights();
    gAlgo = new CGenAlg(CParams::iNumSweepers, CParams::dMutationRate, CParams::dCrossoverRate, numWeightsInNN);
}

void Controller::setMineArray(mines *m[])
{
    for(int i = 0; i < CParams::iNumMines; i++)
    {
       this->a_mines[i] = m[i];
    }
}

void Controller::checkCollision()
{
    for(int i = 0; i < CParams::iNumSweepers; i++)
        for(int j = 0; j < CParams::iNumMines; j++)
            if(mineSweepers[i]->collidesWithItem(a_mines[j]))
                if(a_mines[j] != NULL)
                {
                    mineSweepers[i]->scene()->removeItem(a_mines[j]);
                    double x = RandFloat() * CParams::iWindowWidth;
                    double y = RandFloat() * CParams::iWindowHeight;
                    a_mines[j]->setPos(x,y);
                    v_mines[j] = SVector2D(SVector2D(x,y));
                    mineSweepers[i]->scene()->addItem(a_mines[j]);
                    mineSweepers[i]->increaseFitness(1);
                }
}

int Controller::getGeneration()
{
    return generation;
}

mines** Controller::getMineArray()
{
    return a_mines;
}

Minesweeper** Controller::getMineSweeperArray()
{
    return mineSweepers;
}

double Controller::getHighestFitness()
{
    vector<SGenome> temp = vecThePopulation;
    sort(temp.begin(),temp.end());

    if(bestSweeper.dFitness == NULL || bestSweeper.dFitness < temp[temp.size() - 1].dFitness)
        bestSweeper = temp[temp.size() - 1];

    return temp[temp.size()-1].dFitness;
}

double Controller::getAvgFitness()
{
    double sum;
    vector<SGenome> temp = vecThePopulation;
    for(int i = 0; i < temp.size(); ++i)
        sum += temp[i].dFitness;
    sum = sum/(double)temp.size();

    return sum;
}
